﻿//PlazaRecord.cs
//Copyright (c) 2013 StockSharp LLC, all rights reserved.
//This code module is part of StockSharp library.
//This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3.
//See the file License.txt for the license details.
//More info on: http://stocksharp.com

namespace StockSharp.Plaza
{
	using System;
	using System.Collections.Generic;

	using Ecng.Common;

	using StockSharp.Plaza.Metadata;
	using StockSharp.Plaza.Wrappers;

	/// <summary>
	/// Запись в потоке репликации.
	/// </summary>
	public class PlazaRecord
	{
		private readonly int _replIdIndex;
		private readonly int _replRevIndex;
		private readonly int _replActIndex;

		private readonly object[] _data;

		internal PlazaRecord(PlazaTable table, IRecordWrapper record)
			: this(table)
		{
			if (record == null)
				throw new ArgumentNullException("record");

			for (var i = 0u; i < _data.Length; i++)
			{
				_data[i] = record.GetObject(i);
			}
		}

		private PlazaRecord(PlazaTable table)
		{
			if (table == null)
				throw new ArgumentNullException("table");

			Table = table;

			var columns = Table.Columns;

			_replIdIndex = columns.ReplId.Index;
			_replRevIndex = columns.ReplRevision.Index;
			_replActIndex = columns.ReplAction.Index;

			_data = new object[columns.Count];
		}

		/// <summary>
		/// Уникальный идентификатор записи в таблице.
		/// </summary>
		public long ReplId
		{
			get { return (long)_data[_replIdIndex]; }
		}

		/// <summary>
		/// Уникальный номер изменения в таблице.
		/// </summary>
		public long ReplRevision
		{
			get { return (long)_data[_replRevIndex]; }
		}

		/// <summary>
		/// Признак того, что запись удалена.
		/// </summary>
		public bool ReplDeleted
		{
			get { return (long)_data[_replActIndex] != 0; }
		}

		/// <summary>
		/// Таблица с метаданными, описывающая данные этой записи.
		/// </summary>
		public PlazaTable Table { get; private set; }

		/// <summary>
		/// Количество колонок в записи.
		/// </summary>
		public int Count
		{
			get { return _data.Length; }
		}

		/// <summary>
		/// Перечислитель, содержащий значения колонок записи.
		/// </summary>
		public IEnumerable<object> Values
		{
			get { return _data; }
		}

		private T Get<T>(PlazaColumn column, Func<object, T> converter)
		{
			var index = column.Index;

			if (index >= _data.Length)
			{
				throw new ArgumentOutOfRangeException("column", index, "Колонка {0} таблицы {1} с индексом {2} не присутствует в данных."
					.Put(column.Name, column.TableName, index));
			}

			var value = _data[index];

			try
			{
				return converter(value);
			}
			catch (Exception ex)
			{
				throw new InvalidCastException("Колонка {0} таблицы {1} имеет тип '{2}'. Приведение к типу '{3}' невозможно."
					.Put(column.Name, column.TableName, value == null ? null : value.GetType(), typeof(T)), ex);
			}
		}

		/// <summary>
		/// Получить значение типа <see cref="int"/>.
		/// </summary>
		/// <param name="column">Колонка.</param>
		/// <returns>Значение типа <see cref="int"/>.</returns>
		public int GetInt(PlazaColumn column)
		{
			return Get(column, value => (int)value);
		}

		/// <summary>
		/// Получить значение типа <see cref="long"/>.
		/// </summary>
		/// <param name="column">Колонка.</param>
		/// <returns>Значение типа <see cref="long"/>.</returns>
		public long GetLong(PlazaColumn column)
		{
			return Get(column, value => (long)value);
		}

		/// <summary>
		/// Получить значение типа <see cref="bool"/>.
		/// </summary>
		/// <param name="column">Колонка.</param>
		/// <returns>Значение типа <see cref="bool"/>.</returns>
		public bool GetBool(PlazaColumn column)
		{
			return Get(column, value => (sbyte)value == 1);
		}

		/// <summary>
		/// Получить значение типа <see cref="string"/>.
		/// </summary>
		/// <param name="column">Колонка.</param>
		/// <returns>Значение типа <see cref="string"/>.</returns>
		public string GetString(PlazaColumn column)
		{
			return Get(column, value => (string)value);
		}

		/// <summary>
		/// Получить значение типа <see cref="DateTime"/>.
		/// </summary>
		/// <param name="column">Колонка.</param>
		/// <returns>Значение типа <see cref="DateTime"/>.</returns>
		public DateTime GetDateTime(PlazaColumn column)
		{
			return Get(column, value => ((ulong)value).ToDateTime());
		}

		/// <summary>
		/// Получить значение типа <see cref="decimal"/>.
		/// </summary>
		/// <param name="column">Колонка.</param>
		/// <returns>Значение типа <see cref="decimal"/>.</returns>
		public decimal GetDecimal(PlazaColumn column)
		{
			return Get(column, value =>
			{
				if (value is int)
					return (int)value;
				else
					return ((decimal)value).RemoveTrailingZeros();
			});
		}

		/// <summary>
		/// Получить значение.
		/// </summary>
		/// <typeparam name="T">Тип возвращаемого значения.</typeparam>
		/// <param name="column">Колонка.</param>
		/// <returns>Значение типа <typeparamref name="T" />.</returns>
		public T Get<T>(PlazaColumn column)
		{
			if (typeof(T) == typeof(DateTime))
				return GetDateTime(column).To<T>();
			else if (typeof(T) == typeof(string))
				return GetString(column).To<T>();
			else if (typeof(T) == typeof(bool))
				return GetBool(column).To<T>();
			else if (typeof(T) == typeof(long))
				return GetLong(column).To<T>();
			else if (typeof(T) == typeof(int))
				return GetInt(column).To<T>();
			else if (typeof(T) == typeof(decimal))
				return GetDecimal(column).To<T>();
			else
				return Get(column, value => value.To<T>());
		}
	}
}
